# — coding: utf-8 –
import os
import re
import sys
import traceback
from time import sleep
import uiautomator2 as u2
from xml.etree import ElementTree as ET
import logging

from uiautomator2 import UiObject

from Config import Params
from Decorators.CommonDecorator2 import RecordAction, TryExcept
from Service.MyLogger import MyLogger
from Utils import IOUtil, TimeUtil, RecordUtil, ConvertUtil
from random import random

logFileName = os.path.basename(__file__).split(".")[0]
log = MyLogger(r"./Logs/%s.log" % (logFileName), logging.DEBUG, logging.DEBUG)

from threading import Thread, current_thread
ifNotFoundWaitSeconds = 10
LIKE_RNG = (0,900000000)
COLL_RNG = (0,800000000)
FANS_RNG = (0,700000000)
pkgNameTool = "com.codeboy.qianghongbao"
pkgNameXHS = "com.xingin.xhs"
pkgNameDY = "com.ss.android.ugc.aweme"
pkgNameXQ = ""
whiteList=['249cc885','45caac46','68c5aa3e','d290aa9a','9ec7c44f','N6HBB21610002596']
# whiteList=['45caac46']
blackList=[]
# blackList=['9ec7c44f']
class pItem:
    def __init__(self,action:str,preAct=None,nextAct=None,ind=0,inputContent=None,currActivity:str=None,nextActivity:str=None,isStartPoint:bool=False,remark=None,curInfo=None,slpBfSeconds=0.0):
        self.action = action
        self.preAct = preAct
        self.nextAct = nextAct
        self.ind = ind
        self.inputContent = inputContent
        self.currActivity = currActivity
        self.nextActivity = nextActivity
        self.remark = remark
        self.curInfo = curInfo
        self.isStartPoint = isStartPoint
        self.slpBfSeconds = slpBfSeconds
processArr = [
    # pItem("chooseBranch@arr:['setNextAct@text:手机号登录','setNextAct@text:其他登录方式']",nextAct="notSure",currActivity="WelcomeActivity",nextActivity="LoginActivity",isStartPoint=True),
    pItem("doNothing@d()#",slpBfSeconds=3,nextAct="['click@text:手机号登录','click@text:其他登录方式']",currActivity="WelcomeActivity",nextActivity="LoginActivity",isStartPoint=True),
    pItem("click@text:手机号登录",nextAct="click@text:密码登录",remark="#not_wait_appear;",currActivity="WelcomeActivity",nextActivity="LoginActivity"),
        pItem("click@text:其他登录方式",nextAct="click@pos:(0.216,0.875)",remark="#not_wait_appear;",currActivity="WelcomeActivity",nextActivity="WelcomeActivity"),
        pItem("click@pos:(0.216,0.875)",nextAct="click@text:其他号码登录",remark="手机登录图标",currActivity="WelcomeActivity",nextActivity="LoginActivity"),
        pItem("click@text:其他号码登录",nextAct="click@text:密码登录",currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("click@text:密码登录",nextAct="click@pos:(0.832,0.217)",currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("click@pos:(0.832,0.217)",nextAct="input@text:请输入手机号码",remark="清除手机号码图标",currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("input@text:请输入手机号码",nextAct="input@text:输入密码",inputContent=None,currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("input@text:输入密码",nextAct="click@text:登录",preAct="input@text:请输入手机号码",inputContent=None,currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("click@text:登录",nextAct="click@text:同意并继续",currActivity="LoginActivity",nextActivity="LoginActivity"),
    pItem("click@text:同意并继续",nextAct="click@pos:(0.925,0.072)",currActivity="LoginActivity",nextActivity="LoginActivity"),
    # 已进入到首页↓↓
    pItem("click@pos:(0.925,0.072)",slpBfSeconds=3,nextAct="input@className:android.widget.EditText",remark="首页搜索图标",currActivity="index.v2.IndexActivityV2",nextActivity="GlobalSearchActivity",isStartPoint=True),
    # 暂时忽略 目标用户 清除按钮
    pItem("input@className:android.widget.EditText",nextAct="click@pos:(0.936,0.07)",remark="目标用户id输入框",currActivity="iGlobalSearchActivity",nextActivity="GlobalSearchActivity"),
    pItem("click@pos:(0.936,0.07)",nextAct="click@text:用户",remark="目标用户搜索按钮",currActivity="GlobalSearchActivity",nextActivity="GlobalSearchActivity"),
    pItem("click@text:用户",nextAct="click@textContains:小红书号：",remark="",currActivity="index.v2.IndexActivityV2",nextActivity="index.v2.IndexActivityV2"),
    pItem("click@textContains:小红书号：",nextAct="click@eval:d(text='关注').right(className='android.widget.ImageView')",remark="",currActivity="GlobalSearchActivity",nextActivity="NewOtherUserActivity"),
    pItem("click@eval:d(text='关注').right(className='android.widget.ImageView')",nextAct="input@text:发消息…",remark="",currActivity="GlobalSearchActivity",nextActivity="NewOtherUserActivity"),
    # pItem("click@pos:(0.902, 0.351)",nextAct="click@pos:(0.902, 0.329)",remark="目标用户进入聊天按钮",currActivity="NewOtherUserActivity",nextActivity="ChatActivity"),
    # pItem("click@pos:(0.902, 0.329)",nextAct="click@pos:(0.902, 0.375)",remark="目标用户进入聊天按钮2",currActivity="NewOtherUserActivity",nextActivity="ChatActivity"),
    # pItem("click@pos:(0.902, 0.375)",nextAct="input@text:发消息…",remark="目标用户进入聊天按钮3",currActivity="NewOtherUserActivity",nextActivity="ChatActivity"),
    pItem("input@text:发消息…",nextAct="click@text:发送",remark="",currActivity="ChatActivity",nextActivity="ChatActivity"),
    pItem("click@text:发送",nextAct="doNothing@d()",remark="",currActivity="ChatActivity",nextActivity="ChatActivity"),
    # 对方关注或回复你之前，24小时内最多只能发1条文字消息 or 今天的招呼次数已经用完啦
    pItem("doNothing@d()",nextAct="['doSpecial@textContains:24小时内最多','doSpecial@textContains:今天的招呼次数已经用完啦','doSpecial@text:发消息…']",currActivity="ChatActivity",nextActivity="ChatActivity"),
    pItem("doSpecial@textContains:24小时内最多",slpBfSeconds=1.5,nextAct="doNothing@text:搜索",currActivity="ChatActivity",nextActivity="GlobalSearchActivity"),
    pItem("doSpecial@textContains:今天的招呼次数已经用完啦",nextAct="click@text:我",currActivity="ChatActivity",nextActivity="GlobalSearchActivity"),
    pItem("doSpecial@text:发消息…",nextAct="doNothing@text:搜索",currActivity="ChatActivity",nextActivity="GlobalSearchActivity"),
    pItem("doNothing@text:搜索",nextAct="input@className:android.widget.EditText",remark="ifNotFoundPress@back",currActivity="GlobalSearchActivity",nextActivity="GlobalSearchActivity"),

    pItem("click@text:我",nextAct="click@eval:d(text='编辑资料').right(className='android.widget.ImageView')",remark="ifNotFoundPress@back",currActivity="ChatActivity",nextActivity="IndexActivityV2"),
    pItem("click@eval:d(text='编辑资料').right(className='android.widget.ImageView')",nextAct="scrollDown@d()",currActivity="IndexActivityV2",nextActivity="SettingActivityV2"),
    pItem("scrollDown@d()", nextAct="click@text:退出登录",currActivity="SettingActivityV2", nextActivity="SettingActivityV2"),
    pItem("click@text:退出登录", nextAct="click@text:确定",currActivity="SettingActivityV2", nextActivity="SettingActivityV2"),
    pItem("click@text:确定", nextAct="doNothing@d()#", currActivity="SettingActivityV2",nextActivity="WelcomeActivity"),
]


def pArr2Pdict(pArr:list):
    pDict = {}
    for i in range(len(pArr)):
        pDict[pArr[i].action]=pArr[i]
    return pDict
def pArr2StartPointArr(pArr:list):
    startPointArr = []
    for i in range(len(pArr)):
        pi: pItem = pArr[i]
        if pi.isStartPoint==True:
            startPointArr.append(pi)
    print("yzprt startPointArr:", startPointArr)
    return startPointArr

class U2Syner:
    def __init__(self):
        self.deviceList = []
        self.deviceDict = {}
        self.watchList = None
        self.pArr = processArr
        self.pDict = pArr2Pdict(self.pArr)
        self.startPointArr = pArr2StartPointArr(self.pArr)
        self.isPopLocking = False
        self.isPushLocking = False
        self.getConnectedDeviceList()

    def pop1TargetId(self,filePath: str):
        while self.isPopLocking:
            print("yzprt is pop locking...")
            sleep(1)
        self.isPopLocking = True
        popedContent = IOUtil.popFirstRow(filePath)
        IOUtil.append1Row(IOUtil.getNewFilePathAddingSuff(filePath, "_processing"), popedContent)
        self.isPopLocking = False
        return popedContent
    def push1TargetId(self,filePath: str, popedContent: str):
        while self.isPushLocking:
            print("yzprt is push locking...")
            sleep(1)
        self.isPushLocking = True
        IOUtil.get1RowContaingContent(IOUtil.getNewFilePathAddingSuff(filePath, "_processing"), popedContent,isPop=True)
        # IOUtil.append1Row(IOUtil.getNewFilePathAddingSuff(filePath, "_over"), popedContent)
        self.isPushLocking = False
    def recordSuccess(self, d: u2.Device, res:u2._selector.UiObject):
        filePath = os.path.join(Params.ROOT_DIR, r"raw\user-pswd_record.txt")
        recordContent = "%s\t%s\t%s\t%s\t%s\t%s"%("Success",d.serial,TimeUtil.getFormatTimeNow(),self.pDict["input@text:请输入手机号码"].curInfo,self.pDict["input@className:android.widget.EditText"].curInfo,res.get_text())
        IOUtil.append1Row(filePath,recordContent)

    def recordRepeated(self, d: u2.Device, res:u2._selector.UiObject):
        filePath = os.path.join(Params.ROOT_DIR, r"raw\user-pswd_record.txt")
        recordContent = "%s\t%s\t%s\t%s\t%s\t%s"%("Repeated",d.serial,TimeUtil.getFormatTimeNow(),self.pDict["input@text:请输入手机号码"].curInfo,self.pDict["input@className:android.widget.EditText"].curInfo,res.get_text())
        IOUtil.append1Row(filePath, recordContent)
    def recordAbnormal(self, d: u2.Device, res:u2._selector.UiObject):
        filePath = os.path.join(Params.ROOT_DIR, r"raw\user-pswd_record.txt")
        recordContent = "%s\t%s\t%s\t%s\t%s\t%s"%("Abnormal",d.serial,TimeUtil.getFormatTimeNow(),self.pDict["input@text:请输入手机号码"].curInfo,self.pDict["input@className:android.widget.EditText"].curInfo,res.get_text())
        IOUtil.append1Row(filePath, recordContent)
    def getInputContentByPi(self,pi: pItem):
        action = pi.action
        inputContent = None
        if action == "input@text:请输入手机号码":
            filePath = os.path.join(Params.ROOT_DIR, r"raw\user-pswd.txt")
            popedContent = self.pop1TargetId(filePath)
            if popedContent is not None and popedContent.strip(' ')!="":
                inputContent = popedContent.split("\t")[0]
            else:
                inputContent = None
            pi.curInfo = inputContent
        elif action == "input@text:输入密码":
            filePath = os.path.join(Params.ROOT_DIR, r"raw\user-pswd_processing.txt")
            popedContent = IOUtil.get1RowContaingContent(filePath,self.pDict[pi.preAct].curInfo)
            if popedContent is not None and popedContent.strip(' ') != "":
                inputContent = popedContent.split("\t")[1]
            else:
                inputContent = None
            pi.curInfo = inputContent
        elif action == "input@className:android.widget.EditText" and pi.remark == "目标用户id输入框":
            filePath = os.path.join(Params.ROOT_DIR, r"raw\account.txt")
            popedContent = self.pop1TargetId(filePath)
            if popedContent is not None and popedContent.strip(' ') != "":
                inputContent = popedContent.strip(' ')
            else:
                inputContent = None
            pi.curInfo = inputContent
        elif action == "input@text:发消息…":
            filePath = os.path.join(Params.ROOT_DIR, r"raw\words.txt")
            arr = IOUtil.file2Arr(filePath)
            if arr == None or len(arr) <= 0:
                inputContent = None
            else:
                inputContent = arr[0].strip(' ')
            pi.curInfo = inputContent
        else:
            pass
        return inputContent
    def getConnectedDeviceList(self):
        s = os.popen("adb devices")
        a = s.read()
        list = a.split('\n')
        deviceIdList = []
        for temp in list:
            print("yzprt temp:", temp)
            if len(temp.split()) > 1:
                if temp.split()[1] == 'device':
                    deviceIdList.append(temp.split()[0])
        print('本次共扫描出%s个安卓设备' % len(deviceIdList))
        self.deviceList.clear()
        funcStr = "get1DeviceBySerial"
        threadDict = {}
        for serial in deviceIdList:
            # print(f'yzprt serial in blackList:{serial in blackList}, serial not in whileList:{serial not in whiteList}')
            if serial in blackList:
                continue
            elif serial not in whiteList:
                continue
            threadDict["%s_%s" % (funcStr, serial)] = Thread(target=self.add1DeviceToListBySerial, args=(self.deviceList, serial,), name="t-%s" % (serial))
            threadDict["%s_%s" % (funcStr, serial)].start()
            threadDict["%s_%s" % (funcStr, serial)].join(30)
            print(f'yzprt 1 device connected serial:{serial}')
        # while len(deviceList)<len(deviceIdList):
        #     pass
        print(f"yzprt all devices are connected... size:{len(self.deviceList)}")
        self.deviceList2Dict()
        return self.deviceList
    def deviceList2Dict(self):
        self.deviceDict.clear()
        for i in range(len(self.deviceList)):
            self.deviceDict[self.deviceList[i].serial] = self.deviceList[i]
    def add1DeviceToListBySerial(self,dList:list, serial:str):
        d = u2.connect(serial)
        dList.append(d)
        print("yzprt d.serial:", d.serial)
        print("yzprt d.info:", d.info)
    def get1DeviceBySerial(self, serial:str):
        d = u2.connect(serial)
        print("yzprt d.serial:", d.serial)
        print("yzprt d.info:", d.info)
        return d


    def mStartApp(self, pkgName:str):
        funcStr = "mStartApp"
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s" % (funcStr, d.serial)] = Thread(target=self.oStartApp, args=(d, pkgName),name="t-%s" % (d.serial))
            threadDict["%s_%s" % (funcStr, d.serial)].start()

    @RecordAction
    def oStartApp(self,d: u2.Device, pkgName:str):
        d.app_start(pkgName, wait=True,use_monkey=True)
        # runningApp = d.app_list_running()
        # print(f'yzprt runningApp:{runningApp}')
        # log.info("yzlog serial: %s started %s" % (d.serial, pkgName))
    def mStopApp(self, pkgName:str):
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            d.app_stop(pkgName)
            log.info("yzlog serial: %s started %s"%(d.serial,pkgName))
    def recognizeActivityAndGetPi(self,d: u2.Device):
        resActivity = d.app_current()['activity'] if d.app_current is not None else None
        log.info("yzlog serial: %s, resActivity: %s"%(d.serial,resActivity))
        for i in range(len(self.startPointArr)):
            pi:pItem = self.startPointArr[i]
            print("yzprt pi.currActivity:", pi.currActivity)
            if resActivity.find(pi.currActivity)>=0:
                log.info("yzlog pi.action:", pi.action)
                return pi
        return None
    def mClick(self, x=None, y=None,xy=None, text: str = None):
        funcStr = "mClick"
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s" % (funcStr, d.serial)] = Thread(target=self.oClick, args=(d, x,y,xy,text),name="t-%s" % (d.serial))
            threadDict["%s_%s" % (funcStr, d.serial)].start()

    @RecordAction
    def oClick(self,d:u2.Device, x=None, y=None,xy=None, text: str = None):
        if x is not None and y is not None:
            d.click(x, y)
        elif xy is not None:
            d.click(xy[0], xy[1])
        elif text is not None:
            # print("yzprt d(text=text).info:", d(text=text).info)
            # print("yzprt d(text=text).bounds:", d(text=text).bounds())
            d(text=text).click()
        else:
            log.error("yzlog unknown click type at multiClick:")
    def mPress(self, actStr: str):
        funcStr = "mPress"
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s"%(funcStr,d.serial)] = Thread(target=self.oPress, args=(d,actStr), name="t-%s"%(d.serial))
            threadDict["%s_%s"%(funcStr,d.serial)].start()

    @RecordAction
    def oPress(self, d: u2.Device, actStr:str):
        d.press(actStr)
        # print("yzprt serial: %s, pressed: %s" % (d.serial, actStr))
    def mDumpHierarchy(self):
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            self.oDumpHierarchy(d)
    def oDumpHierarchy(self, d: u2.Device):
        resHierarchy = d.dump_hierarchy()
        print("yzprt serial: %s, resAppCurrent: %s" % (d.serial, resHierarchy))
        return resHierarchy
        # res = d()
        # print("yzprt res:", res, res.count, res.info, type(res), type(d))
    def scroll(self, d: u2.Device):
        x, y = d.window_size()
        x1 = x / 2
        y1 = y * 0.2
        y2 = y * 0.8
        d.swipe(x1, y2, x1, y1)
    def scorllAlittle(self, d:u2.Device):
        x, y = d.window_size()
        x1 = x / 2
        y1 = y * 0.1
        y2 = y * 0.9
        d.swipe(x1, y2, x1, y1,duration=0.1)
    def longClick(self,ele:u2._selector.UiObject):
        ele.long_click()
    def scrollAndDump(self,d: u2.Device,scrollCount=10):
        for i in range(scrollCount):
            self.oDumpHierarchy(d)
            print("yzprt scrollCount, i:", i)
            self.scroll(d)
            sleep(2)
    def mStartWalking(self):
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            pi = self.recognizeActivityAndGetPi(d)
            threadDict[d.serial] = Thread(target=self.walkPi, args=(d,pi), name="t-%s"%(d.serial))
            threadDict[d.serial].start()

    # # uio.get_text()执行速度太慢,弃用
    # def getDyVideoInfo(self, d: u2s.Device):
    #     flComment = d(resourceId='com.ss.android.ugc.aweme:id/comment_layout')
    #     print("yzprt flComment:", flComment.count, flComment.info)
    #     sibs = flComment.sibling()
    #     print("yzprt sibs:", sibs.count)
    #     for i in range(len(sibs)):
    #         uio = sibs[i]
    #         if uio.get_text() != "":
    #             pass
    #             # log.info(f'yzlog i:{i}, uio.info:{uio.info}')
    def filterEmptyText(self,nodeList:list):
        newNodeList = []
        for i in range(len(nodeList)):
            ee = nodeList[i]
            if ee.attrib['text']!='':
                newNodeList.append(ee)
        return newNodeList
    def mScrollDyAndCollect(self):
        funcStr = "mScrollDyAndCollect"
        threadDict = {}
        log.info(f'yzlog self.deviceList:{self.deviceList}')
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s" % (funcStr, d.serial)] = Thread(target=self.oScrollDyAndCollect, args=(d,),name="t-%s" % (d.serial))
            threadDict["%s_%s" % (funcStr, d.serial)].start()
    def oScrollDyAndCollect(self, d: u2.Device):
        d.app_start(pkgNameDY, wait=True)
        maxCollectCount = 500
        collectCount = 0
        defaultWaitTime = 6.0
        while True:
            self.getDyVideoInfoByDH(d)
            ivUserAvater = d(resourceId='com.ss.android.ugc.aweme:id/user_avatar')
            if ivUserAvater.exists:
                ivUserAvater.click()
            else:
                collectCount += 1
                self.scorllAlittle(d)
                sleep(2)
                print(f'yzprt d.serial:{d.serial}, collectCount Abnormal:{collectCount}/{maxCollectCount}, yzError: ivUserAvater not exists!!!!!!!')
                continue
            log.info(f'yzlog 0:{0}, d.serial:{d.serial}')
            isAppear0 = d(resourceId='com.ss.android.ugc.aweme:id/scroll_layout').wait(timeout=defaultWaitTime)
            print(f'yzprt isAppear0:{isAppear0}')
            print(f'yzprt isAppear0.01:{isAppear0}')
            isAppear1 = d(resourceId='com.ss.android.ugc.aweme:id/scroll_layout').wait(timeout=defaultWaitTime)
            print(f'yzprt isAppear1:{isAppear1}')
            # while True:
            #     # isAppear = d(resourceId='com.ss.android.ugc.aweme:id/scroll_layout').wait(timeout=defaultWaitTime)
            #     # sleep(1)
            #     isExists = d(resourceId='com.ss.android.ugc.aweme:id/scroll_layout').exists
            #     print(f'yzprt isAppear00:{isExists} vs isExists:{isExists}')
            #     log.info(f'yzlog isAppear:{isExists} vs isExists:{isExists}')
            #     print(f'yzprt isAppear99:{isExists} vs isExists:{isExists}')
            #     if isExists:
            #         break
            #     sleep(0.5)
            log.info(f'yzlog 1:{1}, d.serial:{d.serial}')
            # 正常情况
            if d(resourceId='com.ss.android.ugc.aweme:id/scroll_layout').exists:
                # d(textContains='抖音号：').wait(timeout=defaultWaitTime)
                self.getDyUserInfoByDH(d)
                d.press("back")
            # 弹出了其它APP的广告
            else:
                print(f'yzprt d.serial:{d.serial}, collectCount Abnormal:{collectCount+1}/{maxCollectCount}, yzError: scroll_layout not exists!!!!!!!')
                d.app_start(pkgNameDY,wait=True)
                d.press("back")
                sleep(2)
            d(resourceId='com.ss.android.ugc.aweme:id/user_avatar').wait(timeout=defaultWaitTime)
            # d(resourceId='com.ss.android.ugc.aweme:id/comment_container').wait(timeout=defaultWaitTime)
            collectCount += 1
            self.scorllAlittle(d)
            # self.flingEle(d(resourceId="com.ss.android.ugc.aweme:id/dialog_container"))
            if collectCount>=maxCollectCount:
                print(f'yzprt d.serial:{d.serial}, collectCount:{collectCount}/{maxCollectCount}, is the last one!!!')
                break
            # flResIdContent = d(resourceId="android:id/content")
            # flResIdContent.swipe("up",steps=1)
            sleepRndSec = round(2.0+random()*3,2)
            print(f'yzprt d.serial:{d.serial}, collectCount:{collectCount}/{maxCollectCount}, sleepRndSec:{sleepRndSec}')
            sleep(sleepRndSec)

    def getDyUserInfoByDH(self, d: u2.Device):
        print(f'yzprt d.serial:{d.serial}')
        xmlContent = d.dump_hierarchy()
        root = ET.fromstring(xmlContent)
        userInfoNode = root.findall('.//node[@resource-id="com.ss.android.ugc.aweme:id/scroll_layout"]/..')[0]
        print(f'yzprt userInfoNode:{userInfoNode.attrib}')
        childNodeList = userInfoNode.findall('.//')
        print(f'yzprt len(childNodeList):{len(childNodeList)}')
        infoList = []
        childNodeList = self.filterEmptyText(childNodeList)
        for i in range(len(childNodeList)):
            ee = childNodeList[i]
            text = ee.attrib['text']
            print(f"yzprt i:{i}, ee.attrib['text']:{ee.attrib['text']}")
            if text in ['获赞','关注','粉丝']:
                if text == '关注' and ee.attrib['index']!='1':
                    continue
                numStr = childNodeList[i-1].attrib['text']
                numInt = ConvertUtil.numStr2numInt(numStr)
                infoList.append(text+"："+str(numInt))
                if text=='粉丝':
                    brief = childNodeList[i+1].attrib['text']
                    brief = brief.replace('\n','\\n').replace('\r','\\r')
                    infoList.append('简介：'+brief)
                    self.dyConc(d,numInt,ee,FANS_RNG,infoList)

            elif text.find('抖音号：')>=0:
                infoList.append(text)
                nickname = childNodeList[i-1].attrib['text']
                infoList.append('昵称：'+nickname)
            elif text.find('IP：')>=0:
                infoList.insert(-1,text)
            elif text.find('作品 ')>=0:
                infoList.insert(-2,text.replace(' ','：'))
            elif text in ['进入橱窗','粉丝群','团购推荐','Ta的音乐','直播动态']:
                # 列数不整齐
                # infoList.append(text+"::"+childNodeList[i+1].attrib['text'])
                # 列数整齐
                if infoList[-1].find("::")>=0:
                    infoList[-1] += ", "+text+"::"+childNodeList[i+1].attrib['text']
                else:
                    infoList.append(text+"::"+childNodeList[i+1].attrib['text'])
        print(f'yzprt infoList:{infoList}')
        RecordUtil.recordInfo(d.serial,infoList,infoTag="InfDyUsr")
    def getDyVideoLink(self, d:u2.Device):
        d(resourceId="com.ss.android.ugc.aweme:id/share_container").click()
        d(text='复制链接').click(timeout=5.0)
        d.app_start(pkgNameTool, wait=True)
        # d.set_clipboard(TimeUtil.getFormatTimeNow())
        d(text="测试按钮").click()
        link = d(resourceId="com.codeboy.qianghongbao:id/et_msg").get_text()
        self.oStartApp(d,pkgNameDY)
        return link

    @TryExcept
    def dyLikeCollConc(d: u2.Device, U: dict, likeRng, collRng, concRng):
        pass
    @TryExcept
    def dyLike(self, d: u2.Device, infoList: list,infoNodeList, likeRng):
        print(f'yzprt infoList:{infoList}')
        print(f'yzprt infoNodeList:{infoNodeList}')
        if infoList[-4] in range(*likeRng):
            # (type): xml.etree.ElementTree.Element
            boundsStr = infoNodeList[-4].attrib['bounds']
            print(f'yzprt boundsStr@dyLike:{boundsStr}')
            posTuple = eval(boundsStr.replace("][",",").replace("[","(").replace("]",")"))
            print(f'yzprt posTuple@dyLike:{posTuple}')
            hCenterUp = self.getHCenterUpByPosTuple(posTuple)
            print(f'yzprt hCenterUp@dyLike:{hCenterUp}')
            d.click(*hCenterUp)
            RecordUtil.recordSthInAct(f"抖音点赞了:: 链接:{infoList[0]}, 点赞数:{infoList[-4]}, 评论数:{infoList[-3]}, 收藏数:{infoList[-2]}",serial=d.serial)

    @TryExcept
    def dyColl(self, d: u2.Device, infoList: list,infoNodeList, collRng):
        if infoList[-2] in range(*collRng):
            boundsStr = infoNodeList[-2].attrib['bounds']
            print(f'yzprt boundsStr@dyColl:{boundsStr}')
            posTuple = eval(boundsStr.replace("][",",").replace("[","(").replace("]",")"))
            print(f'yzprt posTuple@dyColl:{posTuple}')
            hCenterUp = self.getHCenterUpByPosTuple(posTuple)
            print(f'yzprt hCenterUp@dyColl:{hCenterUp}')
            d.click(*hCenterUp)
            RecordUtil.recordSthInAct(f"抖音收藏了:: 链接:{infoList[0]}, 点赞数:{infoList[-4]}, 评论数:{infoList[-3]}, 收藏数:{infoList[-2]}",serial=d.serial)

    @TryExcept
    def dyConc(self, d: u2.Device, numInt:int,node, concRng, infoList):
        if numInt in range(*concRng):
            # boundsStr = node.attrib['bounds']
            # print(f'yzprt boundsStr@dyConc:{boundsStr}')
            # posTuple = eval(boundsStr.replace("][",",").replace("[","(").replace("]",")"))
            # print(f'yzprt posTuple@dyConc:{posTuple}')
            # hCenterUp = self.getHCenterUpByPosTuple(posTuple)
            # print(f'yzprt hCenterUp@dyConc:{hCenterUp}')
            # self.oDumpHierarchy(d)
            d(text="关注",className="android.widget.TextView")[-1].click()
            RecordUtil.recordSthInAct(f"抖音关注了:: infoList:{infoList}",serial=d.serial)
    def getDyVideoInfoByDH(self, d: u2.Device):
        print(f'yzprt d.serial:{d.serial}')
        xmlContent = d.dump_hierarchy()
        # print(f'yzprt xmlContent:{xmlContent}')
        root = ET.fromstring(xmlContent)
        # print(f'yzprt root:{root}')
        # 必须在下面的参数末尾就先"/..", 否则分步获取变量后, 新变量可能就没有parent了
        videoInfoNode = root.findall('.//node[@resource-id="com.ss.android.ugc.aweme:id/comment_layout"]/..')[0]
        print(f'yzprt videoInfoNode:{videoInfoNode.attrib}')
        childNodeList = videoInfoNode.findall('.//')
        print(f'yzprt len(childNodeList):{len(childNodeList)}')
        infoList = []
        infoNodeList = []
        infoList.append(self.getDyVideoLink(d))
        infoNodeList.append(None)
        for i in range(len(childNodeList)):
            ee = childNodeList[i]
            text = ee.attrib['text']
            if text!='':
                if not text in ['不喜欢']:
                    print(f'yzprt i:{i}, ee.attrib:{ee.attrib}')
                    numInt = ConvertUtil.numStr2numInt(text)
                    infoList.append(numInt)
                    infoNodeList.append(ee)
        self.dyLike(d,infoList,infoNodeList, LIKE_RNG)
        self.dyColl(d, infoList, infoNodeList, COLL_RNG)
        print(f'yzprt infoList:{infoList}')
        RecordUtil.recordInfo(d.serial,infoList,infoTag="InfDyVD")
                    

    def walkPi(self, d: u2.Device, pi:pItem):
        if pi.slpBfSeconds is not None and pi.slpBfSeconds>0:
            sleep(pi.slpBfSeconds)
        # 找控件:
        action = pi.action
        res:u2._selector.UiObject = None
        if action.find("@text:") >= 0:
            text = action.split("@text:")[1]
            if pi.remark is not None and pi.remark.find("#not_wait_appear;")>=0:
                pass
            else:
                if pi.remark is not None and pi.remark.find("ifNotFoundPress@") >= 0:
                    ctrlStr = pi.remark.split("ifNotFoundPress@")[1]
                    countSeconds = 0
                    while countSeconds<ifNotFoundWaitSeconds:
                        if d(text=text).exists:
                            break
                        else:
                            d.press(ctrlStr)
                            countSeconds += 0.5
                            print("yzprt press %s %s times for action=%s:"%(ctrlStr,countSeconds,pi.action))
                            sleep(0.5)
                    if countSeconds>=ifNotFoundWaitSeconds:
                        log.error("yzlog can't find action:", pi.action)
                        sys.exit(0)
                else:
                    # print("yzprt wait and timeout: 5")
                    d(text=text).wait(timeout=5)
            res = d(text=text)
        elif action.find("@textContains:") >= 0:
            slpSeconds = 5
            if action.find("doSpecial")>=0:
                slpSeconds=0.2
            text = action.split("@textContains:")[1]
            if pi.remark is not None and pi.remark.find("#not_wait_appear;") >= 0:
                pass
            else:
                d(textContains=text).wait(timeout=slpSeconds)
            res = d(textContains=text)
        elif action.find("@className:") >= 0:
            className = action.split("@className:")[1]
            print("yzprt className:", className)
            res = d(className=className)[pi.ind]
            print("yzprt res@findByClassName:", res)
        elif action.find("@pos:") >= 0:
            res = d(text="let you not exists")
        elif action.find("@arr:") >= 0:
            res = eval(action.split("@arr:")[1])
        elif action.find("@d()") >= 0:
            res = d()
        elif action.find("@eval:") >= 0:
            evalStr = action.split("@eval:")[1]
            print("yzprt evalStr:", evalStr)
            res = eval(evalStr)
            print("yzprt res of eval:", res)
        if res is None:
            log.info("yzlog res isNone:",pi.action)
            return None
        elif not res.exists:
            if action.find("@pos:")>=0:
                log.info("yzlog res is @pos type:",pi.action)
            else:
                log.info("yzlog res not exists:", pi.action)
                return None
        # 执行控件的操作:
        if action.find("click@") >= 0:
            if action.find("text") >= 0:
                res.click()
            elif action.find("@pos") >= 0:
                xyTuple = eval(action.split("@pos:")[1])
                # print("yzprt type(xyTuple):", type(xyTuple))
                d.click(xyTuple[0],xyTuple[1])
        elif action.find("input@") >= 0:
            # content = action.split("#val:")[-1]
            content = self.getInputContentByPi(pi)
            print("yzprt content:", content)
            res.wait(timeout=5)
            res.send_keys(content)
        elif action.find("doNothing@") >= 0:
            pass
        elif action.find("doSpecial@") >= 0:
            # ['doSpecial@textContains:24小时内最多','doSpecial@textContains:今天的招呼次数已经用完啦','doSpecial@text:发消息…']
            if action.find("doSpecial@textContains:24小时内最多")>=0:
                self.recordSuccess(d,res)
            elif action.find("doSpecial@textContains:今天的招呼次数已经用完啦")>=0:
                self.recordRepeated(d,res)
            elif action.find("doSpecial@text:发消息…")>=0:
                self.recordSuccess(d,res)
        elif action.find("scrollDown") >= 0:
            self.scroll(d)
        # 执行下一个pItem:
        if pi.nextAct is None:
            return pi
        elif pi.nextAct.startswith("[") and pi.nextAct.endswith("]"):
            nextActArr = eval(pi.nextAct)
            for i in range(len(nextActArr)):
                res = self.walkPi(d,self.pDict[nextActArr[i]])
                if res is not None:
                    break
            return res
        else:
            return self.walkPi(d,self.pDict[pi.nextAct])
    def mSetWatcher(self):
        funcStr = "mSetWatcher"
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s" % (funcStr, d.serial)] = Thread(target=self.oSetWatcher, args=(d,),name="t-%s" % (d.serial))
            threadDict["%s_%s" % (funcStr, d.serial)].start()
            # self.oSetWatcher(d)
    def oSetWatcher(self,d: u2.Device):
        d.watcher.when("打开辅助服务").press("back")
        d.watcher.when("立即安装").press("back")
        d.watcher.when("加入购物车").press("back")
        d.watcher.when("添加搜索到桌面").press("back")
        d.watcher.start()
    def mRemoveWatcher(self):
        funcStr = "mRemoveWatcher"
        threadDict = {}
        for k in range(len(self.deviceList)):
            d: u2.Device = self.deviceList[k]
            threadDict["%s_%s" % (funcStr, d.serial)] = Thread(target=self.oRemoveWatcher, args=(d,),name="t-%s" % (d.serial))
            threadDict["%s_%s" % (funcStr, d.serial)].start()
            # self.oRemoveWatcher(d)
    def oRemoveWatcher(self,d: u2.Device):
        d.watcher.stop()
        d.watcher.remove()

    def getCenter(self, ele:u2._selector.UiObject):
        return ((ele.bounds()[0]+ele.bounds()[2])//2,(ele.bounds()[1]+ele.bounds()[3])//2)
    def getHCenterUp(self,ele:u2._selector.UiObject):
        return ((ele.bounds()[0]+ele.bounds()[2])//2,ele.bounds()[1])
    def getHCenterUpByPosTuple(self,posTuple):
        return ((posTuple[0]+posTuple[2])//2,posTuple[1])
    def flingEle(self, ele:u2._selector.UiObject):
        ele.fling.vert.forward()
        # print(f'yzprt: testFling')



if __name__ == '__main__':
    # pkgName = "com.xingin.xhs"
    # pkgName = "com.github.uiautomator"
    # pkgName = "com.tencent.mm"
    u2=U2Syner()
    u2.mSetWatcher()
    u2.mStartApp(pkgNameDY)
    u2.mScrollDyAndCollect()
    print(f'yzprt end...')

    # 上面的是非阻塞的话,调用下面这行就不监听了
    # u2s.mRemoveWatcher()

    # for d in u2s.deviceList:
    #     u2s.getDyUserInfoByDH(d)
    # u2s.mStopApp(pkgName)
    # u2s.mStartApp(pkgName)

    # u2s.recognizeActivityAndGetPi()
    # u2s.mDumpHierarchy()
    # u2s.mStartWalking()

    # for i in range(len(processArr)):
    #     pi = processArr[i]
    #     action = pi.action
    #     if action.find("click@")>=0:
    #         if action.find("@text:")>=0:
    #             text = action.split("@text:")[1]
    #             u2s.mClick(text=text)
    #         elif action.find("@pos")>=0:
    #             xyTuple = eval(action.split("@pos:")[1])
    #             print("yzprt type(xyTuple):", type(xyTuple))
    #             u2s.mClick(xy=xyTuple)
    #     elif action.find("input@")>=0:
    #         pass
    #     log.info("yzlog action:%s passed:",i, action)
    #     sleep(3)

    # def setWatchers(self, watchList: list):
    #     self.watchList = watchList
    #     for k in range(len(self.deviceList)):
    #         d: u2s.Device = self.deviceList[k]
    #         for i in range(len(watchList)):
    #             xPathWatcher = d.watcher.when(watchList[i])
    #             xPathWatcher.call(lambda d: self.updateWatchers())
    #
    # # 目前仅内部使用
    # def updateWatchers(self):
    #     for k in range(len(self.deviceList)):
    #         d: u2s.Device = self.deviceList[k]
    #         text = self.watchList.pop(0)
    #         d(text=text).click()
    #         d.watcher.reset()
    #         self.setWatchers(self.watchList)
    #         d.watcher.start()
    #
    # def startWatchers(self):
    #     for k in range(len(self.deviceList)):
    #         d: u2s.Device = self.deviceList[k]
    #         d.watcher.start()
